//
// Copyright 2021 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// stylelint-disable selector-class-pattern --
// Selector '.mdc-*' should only be used in this project.

@use 'sass:math';
@use 'sass:map';
@use 'sass:meta';
@use '@material/density/functions' as density-functions;
@use '@material/density/variables' as density-variables;
@use '@material/elevation/elevation-theme';
@use '@material/feature-targeting/feature-targeting';
@use '@material/focus-ring/focus-ring';
@use '@material/ripple/ripple-theme';
@use '@material/rtl/rtl';
@use '@material/dom/dom';
@use '@material/theme/custom-properties';
@use '@material/theme/keys';
@use '@material/theme/state';
@use '@material/theme/theme';
@use '@material/theme/theme-color';
@use '@material/touch-target/mixins' as touch-target-mixins;

$ripple-target: '.mdc-icon-button__ripple';

$icon-size: 24px !default;
$size: 48px !default;
$minimum-height: 28px !default;
$maximum-height: $size !default;
$container-shape: 50%;
$density-scale: density-variables.$default-scale !default;
$density-config: (
  size: (
    default: $size,
    maximum: $maximum-height,
    minimum: $minimum-height,
  ),
) !default;

$_custom-property-prefix: 'icon-button';

$light-theme: (
  disabled-icon-color: theme-color.$on-surface,
  disabled-icon-opacity: 0.38,
  icon-color: theme-color.$primary,
  icon-size: $icon-size,
  focus-icon-color: theme-color.$primary,
  focus-state-layer-color: theme-color.$primary,
  focus-state-layer-opacity: 0.12,
  hover-icon-color: theme-color.$primary,
  hover-state-layer-color: theme-color.$primary,
  hover-state-layer-opacity: 0.08,
  pressed-icon-color: theme-color.$primary,
  pressed-state-layer-color: theme-color.$primary,
  pressed-state-layer-opacity: 0.12,
  state-layer-size: $size,
  focus-ring-color: null,
  focus-ring-offset: 0,
);

@mixin theme($theme) {
  @include theme.validate-theme($light-theme, $theme);

  @include keys.declare-custom-properties(
    $theme,
    $prefix: $_custom-property-prefix
  );
}

@mixin theme-styles($theme) {
  @include theme.validate-theme-styles($light-theme, $theme);

  $theme: keys.create-theme-properties(
    $theme,
    $prefix: $_custom-property-prefix
  );

  @include _state-layer-size($size: map.get($theme, state-layer-size));
  @include _icon-size(map.get($theme, icon-size));
  @include _disabled-icon-opacity(map.get($theme, disabled-icon-opacity));
  @include _icon-color-with-map(
    (
      default: map.get($theme, icon-color),
      disabled: map.get($theme, disabled-icon-color),
      focus: map.get($theme, focus-icon-color),
      hover: map.get($theme, hover-icon-color),
      pressed: map.get($theme, pressed-icon-color),
    )
  );

  // States styles
  @include ripple-theme.theme-styles(
    (
      focus-state-layer-color: map.get($theme, focus-state-layer-color),
      focus-state-layer-opacity: map.get($theme, focus-state-layer-opacity),
      hover-state-layer-color: map.get($theme, hover-state-layer-color),
      hover-state-layer-opacity: map.get($theme, hover-state-layer-opacity),
      pressed-state-layer-color: map.get($theme, pressed-state-layer-color),
      pressed-state-layer-opacity: map.get($theme, pressed-state-layer-opacity),
    ),
    $ripple-target: $ripple-target
  );

  @include ripple-theme.states-colors(
    (
      hover: map.get($theme, hover-state-layer-color),
      press: map.get($theme, pressed-state-layer-color),
    ),
    $ripple-target: $ripple-target
  );

  @include _focus-ring-color(map.get($theme, 'focus-ring-color'));
  @include _focus-ring-offset(map.get($theme, 'focus-ring-offset'));
}

///
/// Sets the density scale for icon button.
///
/// @param {Number | String} $density-scale - Density scale value for component.
///     Supported density scale values range from `-5` to `0`, with `0` being the default.
///
@mixin density($density-scale, $query: feature-targeting.all()) {
  $size: density-functions.prop-value(
    $density-config: $density-config,
    $density-scale: $density-scale,
    $property-name: size,
  );

  @include size($size, $query: $query);
}

///
/// Sets the size of the icon-button.
///
/// @param {Number} $size - Size value for icon-button.
///     Size will set the width, height, and padding for the overall component.
///
@mixin size($size, $query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  @include feature-targeting.targets($feat-structure) {
    width: $size;
    height: $size;
    padding: calc(($size - $icon-size) / 2);
  }

  &.mdc-icon-button--reduced-size {
    $component-size: $size;
    // Icon button ripple size is capped at 40px for icon buttons with
    // densities -1 and 0 (icon buttons with sizes 44x44 and 48x48px).
    // See http://b/192353968 for more info.
    @if math.unit($size) == 'px' and ($size >= 40px and $size <= 48px) {
      $component-size: 40px;
    }

    .mdc-icon-button__ripple {
      @include feature-targeting.targets($feat-structure) {
        width: $component-size;
        height: $component-size;
      }

      @include touch-target-mixins.margin(
        $component-height: $component-size,
        $component-width: $component-size,
        $touch-target-height: $size,
        $touch-target-width: $size,
        $query: $query
      );
    }

    .mdc-icon-button__focus-ring {
      @include feature-targeting.targets($feat-structure) {
        max-height: $component-size;
        max-width: $component-size;
      }
    }
  }

  .mdc-icon-button__touch {
    @include touch-target-mixins.touch-target(
      $set-width: true,
      $query: $query,
      $height: $size,
      $width: $size
    );
  }
}

///
/// Sets the width, height and padding of icon button. Also changes the size of
/// the icon itself based on button size.
///
/// @param {Number} $width - Width value for icon-button.
/// @param {Number} $height - Height value for icon-button. (default: $width)
/// @param {Number} $padding - Padding value for icon-button. (default: max($width, $height) / 2)
/// @deprecated
///     This mixin provides too much of low level customization.
///     Please use mdc-icon-button-size instead.
///
@mixin icon-size(
  $width,
  $height: $width,
  $padding: math.div(math.max($width, $height), 2),
  $query: feature-targeting.all()
) {
  $feat-structure: feature-targeting.create-target($query, structure);

  $component-width: $width + $padding * 2;
  $component-height: $height + $padding * 2;

  @include feature-targeting.targets($feat-structure) {
    width: $component-width;
    height: $component-height;
    padding: $padding;
    font-size: math.max($width, $height);
  }

  svg,
  img {
    @include feature-targeting.targets($feat-structure) {
      width: $width;
      height: $height;
    }
  }
}

///
/// Sets the font color and the ripple color to the provided color value.
/// @param {Color} $color - The desired font and ripple color.
///
@mixin ink-color($color, $query: feature-targeting.all()) {
  @if $color {
    @include ink-color_($color, $query: $query);
    @include ripple-theme.states(
      $color,
      $query: $query,
      $ripple-target: $ripple-target
    );
  }
}

///
/// Flips icon only in RTL context.
///
@mixin flip-icon-in-rtl($query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  .mdc-button__icon {
    @include rtl.rtl {
      @include feature-targeting.targets($feat-structure) {
        @include rtl.ignore-next-line();
        transform: rotate(180deg);
      }
    }
  }
}

///
/// Sets the font color to the provided color value for a disabled icon button.
/// @param {Color} $color - The desired font color.
///
@mixin disabled-ink-color($color, $query: feature-targeting.all()) {
  @include if-disabled_ {
    @include ink-color_($color, $query: $query);
  }
}

///
/// Includes ad-hoc high contrast mode support.
///
@mixin high-contrast-mode-shim($query: feature-targeting.all()) {
  $feat-structure: feature-targeting.create-target($query, structure);

  @include feature-targeting.targets($feat-structure) {
    // TODO(b/175806874): Use the DOM border mixin after the ripple is moved
    // away from :before to a dedicated element.
    outline: solid 3px transparent;

    &:focus {
      outline: double 5px transparent;
    }
  }
}

///
/// Sets the font color to the provided color value. This can be wrapped in
/// a state qualifier such as `mdc-icon-button-if-disabled_`.
/// @access private
///
@mixin ink-color_($color, $query: feature-targeting.all()) {
  $feat-color: feature-targeting.create-target($query, color);

  @include feature-targeting.targets($feat-color) {
    @include theme.property(color, $color);
  }
}

@mixin _state-layer-size($size) {
  @include theme.property(height, $size);
  @include theme.property(width, $size);

  @if $size {
    $value: custom-properties.get-declaration-value($size);
    @if type-of($value) == 'number' {
      @include size($value);
    }
  }
}

@mixin _icon-size($size) {
  .mdc-button__icon {
    @include theme.property(font-size, $size);
  }
  svg,
  img {
    @include theme.property(width, $size);
    @include theme.property(height, $size);
  }
}

///
/// Sets the icon opacity to the given opacity.
/// @access private
///
@mixin _disabled-icon-opacity($opacity) {
  &:disabled {
    @include theme.property(opacity, $opacity);
  }
}

///
/// Sets the icon color to the given color.
/// @param {map} $color-map - The desired icon color, specified as a map of
///     colors with states {default, disabled, focus, hover, pressed} as keys.
/// @access private
///
@mixin _icon-color-with-map($color-map) {
  @include ink-color_(state.get-default-state($color-map));

  $focus: state.get-focus-state($color-map);
  @if $focus {
    @include ripple-theme.focus {
      @include ink-color_($focus);
    }
  }

  $hover: state.get-hover-state($color-map);
  @if $hover {
    &:hover {
      @include ink-color_($hover);
    }
  }

  $pressed: state.get-pressed-state($color-map);
  @if $pressed {
    @include ripple-theme.active {
      @include ink-color_($pressed);
    }
  }

  $disabled: state.get-disabled-state($color-map);
  @if $disabled {
    &:disabled {
      @include ink-color_($disabled);
    }
  }
}

@mixin _states-colors($color-map) {
  // TODO(b/191298796): support focused & pressed key colors.

  $hover: map.get($color-map, hover);
  @if $hover {
    @include ripple-theme.states-base-color(
      $color: $hover,
      $ripple-target: $ripple-target
    );
  }
}

@mixin _focus-ring-color($color, $query: feature-targeting.all()) {
  $color-value: if(
    custom-properties.is-custom-prop($color),
    custom-properties.get-declaration-value($color),
    $color
  );

  @if $color != null {
    .mdc-icon-button__focus-ring {
      @include focus-ring.focus-ring-color(
        $inner-ring-color: $color-value,
        $query: $query
      );
    }
  }
}

@mixin _focus-ring-offset($offset, $query: feature-targeting.all()) {
  $offset-value: if(
    custom-properties.is-custom-prop($offset),
    custom-properties.get-declaration-value($offset),
    $offset
  );

  @if type-of($offset-value) == 'number' {
    .mdc-icon-button__focus-ring {
      @include focus-ring.focus-ring-offset(
        $offset: $offset-value,
        $query: $query
      );
    }
  }
}

///
/// Helps style the icon button in its disabled state.
/// @access private
///
@mixin if-disabled_ {
  &:disabled {
    @content;
  }
}
